use std::fmt::Display;

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use serde::Serialize;

#[derive(Debug, Clone, Serialize)]
pub struct ErrorResponse {
    pub message: String,
}

pub enum ErrorKind {
    EmptyPassword,
    ExceedMaxPasswordLength(usize),
    HashError,
    InvalidHashFormat,
    TokenNotProvided,
    InvalidToken,
    UserNotExist,
}

impl Display for ErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ErrorKind::EmptyPassword => write!(f, "Password can not be empty"),
            ErrorKind::ExceedMaxPasswordLength(max_length) => {
                write!(f, "Password must not be more then {}", max_length)
            }
            ErrorKind::HashError => write!(f, "Error while hashing password"),
            ErrorKind::InvalidHashFormat => write!(f, "Invalid hash password format"),
            ErrorKind::TokenNotProvided => write!(f, "You are not logged in"),
            ErrorKind::InvalidToken => write!(f, "Invalid token"),
            ErrorKind::UserNotExist => write!(f, "User no longer exists"),
        }
    }
}

#[derive(Debug, Clone)]
pub struct HttpError {
    pub message: String,
    pub status_code: StatusCode,
}

impl HttpError {
    pub fn server_error(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
            status_code: StatusCode::INTERNAL_SERVER_ERROR,
        }
    }

    pub fn bad_request(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
            status_code: StatusCode::BAD_REQUEST,
        }
    }

    pub fn unauthorized(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
            status_code: StatusCode::UNAUTHORIZED,
        }
    }

    fn into_http_response(self) -> Response {
        let json_response = Json(ErrorResponse {
            message: self.message,
        });

        (self.status_code, json_response).into_response()
    }
}

impl Display for HttpError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "HTTP Error: message: {}, status code: {}",
            self.message, self.status_code
        )
    }
}

impl IntoResponse for HttpError {
    fn into_response(self) -> Response {
        self.into_http_response()
    }
}
